﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.Search;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace WebRole1
{
    public partial class Result : System.Web.UI.Page
    {
        // account
        static CloudStorageAccount dataAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
        static CloudStorageAccount indexAccount = CloudStorageAccount.FromConfigurationSetting("IndexConnectionString");

        // client
        static CloudBlobClient dataClient = dataAccount.CreateCloudBlobClient();
        static CloudBlobClient indexClient = indexAccount.CreateCloudBlobClient();
        static CloudQueueClient queueClient = indexAccount.CreateCloudQueueClient();

        // container
        static CloudBlobContainer scoreContainer = indexClient.GetContainerReference("score");

        // table context
        static TotalContext context = new TotalContext(indexAccount.TableEndpoint.ToString(), indexAccount.Credentials);

        public List<Document> docList = new List<Document>();

        private const int PARTITION = 200;
        private int blobNum = 1000;
        private int num = 5;
 
        protected void Page_PreRender(object sender, EventArgs e)
        {
            SearchButton.Attributes.Add("onmouseover", "this.style.cursor = 'pointer';"
                                                     + "this.style.background = '#3a5b8d';");
            SearchButton.Attributes.Add("onmouseout", "this.style.cursor = 'default';"
                                                     + "this.style.background = '#4b6c9e';");

            SearchText.Text = Request.Params["q"];

            if (SearchText.Text != null && SearchText.Text != "")
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                DoSearch();
                watch.Stop();
                Debug.WriteLine("Total Time: " + watch.ElapsedMilliseconds + " ms\n");
            }
        }

        protected void Search(object sender, EventArgs e)
        {
            Response.Redirect("search.aspx?q=" + SearchText.Text);
        }

        public void MakeQuery(List<String> queryTerms, String msgID)
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            String message = msgID;
            for (int i = 0; i < queryTerms.Count; i++)
                message += "," + queryTerms[i];

            CloudQueue queryQueue = queueClient.GetQueueReference("query");
            for (int i=0; i < num; i++)
            {
                CloudQueueMessage m = new CloudQueueMessage(blobNum + "," + i * PARTITION + "," + message);
                queryQueue.AddMessage(m);
            }
            watch.Stop();
            Debug.WriteLine("Make Query Time: " + watch.ElapsedMilliseconds + " ms");
        }

        protected void DoSearch()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();

            System.Guid newid = System.Guid.NewGuid();
            String msgID = newid.ToString();

            Dictionary<String, String> result = new Dictionary<string, string>();
            CloudQueueClient queueClient = indexAccount.CreateCloudQueueClient();
            CloudQueue scoreQueue = queueClient.GetQueueReference("score");

            // parse query
            List<String> queryTerms = QueryParser.Parse(SearchText.Text);

            // send query
            MakeQuery(queryTerms, msgID);

            // <url, score>
            Dictionary<string, double> scoreDic = new Dictionary<string, double>();
            int count = 0;

            // wait and receive score from all query work roles
            while (true)
            {
                CloudQueueMessage m = scoreQueue.GetMessage();

                if (m != null)
                {
                    Debug.WriteLine(m.AsString);
                    String[] info = Tokenizer.CommaTokenize(m.AsString);
                    String messageID = info[0];
                    String partition = info[1];

                    if (!messageID.Equals(msgID))
                        continue;

                    scoreQueue.DeleteMessage(m);
                    count++;

                    // no result in this partition
                    if (info.Length > 2)
                        goto end;

                    String blobName = m.AsString;
                    CloudBlob blob = scoreContainer.GetBlobReference(blobName);
                    String scoreInfo = blob.DownloadText();
                    blob.DeleteIfExists();

                    String[] items = Tokenizer.LineTokenize(scoreInfo);

                    if (items != null && items[0] != "")
                    {
                        foreach (String item in items)
                        {
                            String[] pair = Tokenizer.CommaTokenize(item);
                            string url = pair[0];

                            double score = Convert.ToDouble(pair[1]);

                            if (scoreDic.ContainsKey(url))
                                scoreDic[url] += score;
                            else
                                scoreDic.Add(url, score);
                        }
                    }

                end:if (count == num)
                        break;
                }
            }

            // top rank documents
            // LinQ, get top 10 documents
            // result => <docID, score>
            Stopwatch wa = new Stopwatch();
            wa.Start();

            var top = from pair in scoreDic.OrderByDescending(score => score.Value).Take(10) select pair;

            List<String> documents = new List<string>();
            foreach (KeyValuePair<string, double> pair in top)
            {
                // remove documents with score 0, <docID, url>
                if (pair.Value > 0)
                {
                    documents.Add(pair.Key);
                }
            }
            Debug.WriteLine("order time: " + wa.ElapsedMilliseconds + " ms");
            wa.Restart();

            // get corresponding context lines
            List<String> contextLines = GetContextLines(queryTerms, documents);
            Debug.WriteLine("context time: " + wa.ElapsedMilliseconds + " ms");

            for (int i = 0; i < documents.Count; i++)
            {
                result.Add(documents[i], contextLines[i]);
            }

            foreach (KeyValuePair<String, String> pair in result)
            {
                String url = pair.Key;
                String name = url.Substring(url.LastIndexOf("/") + 1);
                docList.Add(new Document()
                {
                    name = name,
                    url = url,
                    content = pair.Value
                });
            }

            DoPageChanged();
        }

        public List<String> GetContextLines(List<String> QueryTerms, List<String> result)
        {
            List<String> contextLines = new List<String>();

            foreach (String url in result)
            {
                CloudBlob blob = dataClient.GetBlobReference(url);
                String content = blob.DownloadText();
                String contextLine = "";

                bool found = false;
                String[] lines = Tokenizer.LineTokenize(content);
                foreach (String line in lines)
                {
                    foreach (String term in QueryTerms)
                    {
                        String lineCopy = line.ToLower();
                        if (lineCopy.IndexOf(term) != -1)
                        {
                            lineCopy = Filter.NonwordsFilter(lineCopy);
                            String[] words = Tokenizer.WhitespaceTokenize(lineCopy);
                            if (words.Contains(term))
                            {
                                contextLine = line;
                                found = true;
                                break;
                            }
                        }
                    }

                    if (found)
                        break;
                }

                contextLines.Add(contextLine);

            }
            return contextLines;
        }

        protected void PageChanged(object src, EventArgs e)
        {
            DoPageChanged();
        }

        protected void DoPageChanged()
        {
            PagedDataSource Pagedatas = new PagedDataSource();
            Pagedatas.DataSource = docList;
            Pagedatas.AllowPaging = true;
            AspNetPager1.RecordCount = docList.Count;
            Pagedatas.CurrentPageIndex = AspNetPager1.CurrentPageIndex - 1;
            Pagedatas.PageSize = AspNetPager1.PageSize;

            gridBlob.DataSource = Pagedatas;
            gridBlob.DataBind();
        }

        protected void ConstructIndex(object sender, EventArgs e)
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();

            blobNum = 0;
            IEnumerable<CloudBlobContainer> containers = dataClient.ListContainers();
            Debug.WriteLine("container Number: " + containers.ToList().Count);
            foreach (CloudBlobContainer container in containers)
            {
                blobNum += container.ListBlobs().ToList().Count;
                if (blobNum >= 1000)
                    break;
            }
            Debug.WriteLine("blob Number: " + blobNum);
            num = (blobNum - 1) / PARTITION + 1;

            // control parser
            CloudQueue parserQueue = queueClient.GetQueueReference("parser");
            parserQueue.CreateIfNotExist();

            String partition = "";
            for (int i = 0; i < num - 1; i++)
            {
                String message = i * PARTITION + "," + ((i + 1) * PARTITION - 1);
                partition += i * PARTITION + ",";
                CloudQueueMessage msg = new CloudQueueMessage(message);
                parserQueue.AddMessage(msg);
            }
            // last message
            String lastMessage = (num - 1) * PARTITION + "," + (blobNum - 1);
            partition += (num - 1) * PARTITION;

            CloudQueueMessage lastmsg = new CloudQueueMessage(lastMessage);
            parserQueue.AddMessage(lastmsg);
            Debug.WriteLine("Assign Parser");

            context.Add(blobNum, partition);

            // receive from inverter
            CloudQueue masterQueue = queueClient.GetQueueReference("master");
            masterQueue.CreateIfNotExist();
            int count = 0;
            while (true)
            {
                CloudQueueMessage m = masterQueue.GetMessage();
                if (m != null)
                {
                    Debug.WriteLine(m.AsString);
                    masterQueue.DeleteMessage(m);
                    count++;

                    if (count == num)
                    {
                        Debug.WriteLine("*************Index Construction Completely*************");
                        break;
                    }
                }
            }
            watch.Stop();
            Debug.WriteLine("Total Time: " + watch.ElapsedMilliseconds + " ms");
        }

        protected void UploadFiles(object sender, EventArgs e)
        {
            String path = "D:\\IR\\data\\20_newsgroups\\20_newsgroups";
            String[] directories = Directory.GetDirectories(path);
            foreach (String directory in directories)
            {
                // create container
                String name = directory.Substring(directory.LastIndexOf('\\') + 1);
                Regex wordReg = new Regex("[^a-z]");
                name = wordReg.Replace(name, "");
                CloudBlobContainer container = dataClient.GetContainerReference(name);
                container.CreateIfNotExist();

                BlobContainerPermissions permissions = container.GetPermissions();
                permissions.PublicAccess = BlobContainerPublicAccessType.Container;
                container.SetPermissions(permissions);

                // upload files
                String[] documents = Directory.GetFiles(directory);
                foreach (String document in documents)
                {
                    String blobName = document.Substring(document.LastIndexOf('\\') + 1);
                    CloudBlob blob = container.GetBlobReference(blobName);
                    blob.Properties.ContentType = "text/plain";
                    blob.UploadFile(document);
                }
            }
        }
    }
}